/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_cloud_heartbeat.h"
#include <stddef.h>
#include "cJSON.h"
#include "securec.h"
#include "ola_def.h"
#include "ola_log.h"
#include "ola_error.h"
#include "ola_base_utils.h"
#include "ola_coap_net.h"
#include "ola_link.h"
#include "ola_cloud_def.h"
#include "ola_cloud_common.h"
#include "ola_cloud_token.h"

static struct {
    unsigned long long lastCheckTime; /* 心跳监测最后一次时间 单位：ms */
    unsigned int checkCnt; /* 心跳监测计数 */
} g_all; /* 当前模块所有控制数据 */

static void CloudHeartbeatResponseHandler(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    if (pkt == NULL) {
        OlaLogWarning("invalid param");
        return;
    }
    int ret;

    do {
        ret = CloudResponseCoapCheck(session, pkt);
        if (ret != OLA_OK) {
            OlaLogWarning("cloud heartbeat response packet error");
            break;
        }
        ret = CloudResponseErrcodeHandler(&pkt->payload);
        if (ret != OLA_OK) {
            OlaLogWarning("cloud heartbeat response errcode error");
            break;
        }
        g_all.checkCnt = 0;
    } while (false);

    return;
}

static int OlaBuildHeartbeatReq(OlaData *data, OlaCoapParam *param)
{
    cJSON *dataJson = cJSON_CreateObject();
    if (dataJson == NULL) {
        OlaLogError("create json error");
        return OLA_CJSON_ERR_CREATE_OBJECT;
    }

    char *payload = cJSON_PrintUnformatted(dataJson);
    if (payload == NULL) {
        OlaLogError("get json payload error");
        cJSON_Delete(dataJson);
        return OLA_CJSON_ERR_PRINT_UNFORMATTED;
    }
    data->data = (unsigned char *)payload;
    data->len = strlen(payload);
    OlaBuildCloudRequestCoapParam(param, OLA_COAP_URI_HEARTBEAT, CloudHeartbeatResponseHandler, true);
    cJSON_Delete(dataJson);
    return OLA_OK;
}

static int CloudHeartbeatRequest(void)
{
    int ret;
    OlaData sendData = {NULL, 0};
    OlaCoapParam param;

    do {
        (void)memset_s(&param, sizeof(OlaCoapParam), 0, sizeof(OlaCoapParam));
        ret = OlaBuildHeartbeatReq(&sendData, &param);
        if (ret != OLA_OK) {
            OlaLogWarning("build heart beat error");
            break;
        }
        ret = OlaSendDataToCloud(&sendData, &param);
        if (ret != OLA_OK) {
            OlaLogWarning("cloud heartbeat request send error");
            break;
        }
    } while (false);

    OlaDataFree(&sendData);
    return ret;
}

/*
 * 功能: 心跳模块初始化
 * 返回: 0，成功
 *       非0，错误码
 * 注意: 在进入上线前调用一次
 */
int CloudHeartbeatInit(void)
{
    int ret;

    g_all.checkCnt = 0;
    ret = OlaGetTime(&g_all.lastCheckTime);
    if (ret != OLA_OK) {
        OlaLogWarning("get time error");
    }

    return ret;
}

/*
 * 功能: 心跳模块处理
 * 返回: 0，成功
 *       非0，错误码
 * 注意: 在上线状态调用
 */
int CloudHeartbeatProcess(void)
{
    int ret;
    bool isTimeout = false;
    do {
        ret = OlaIsOvertime(&isTimeout, g_all.lastCheckTime, CLOUD_HEARTBEAT_TIMEVAL);
        if (ret != OLA_OK) {
            OlaLogWarning("is overtime error");
            break;
        }
        if (isTimeout) {
            if (g_all.checkCnt >= CLOUD_HEARTBEAT_MAX_TIMES) {
                OlaLogWarning("cloud heartbeat timeout");
                ret = OLA_CLOUD_ERR_HEARBEAT_TIMEOUT;
                break;
            }
            ret = CloudHeartbeatRequest();
            if (ret != OLA_OK) {
                OlaLogWarning("send heartbeat request error");
                break;
            }
            g_all.checkCnt++;
            OlaLogInfo("cloud heartbeat send cnt=%u", g_all.checkCnt);
            ret = OlaGetTime(&g_all.lastCheckTime);
            if (ret != OLA_OK) {
                OlaLogWarning("get time error");
                break;
            }
        }
    } while (false);

    return ret;
}